package com.wubo.common.data;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

import com.wubo.common.jdbc.SQLExecutor;
import com.wubo.common.utils.NumberUtils;

/** 数据备份文件恢复管理
 * @author WuBo
 * @CreateDate 2010-9-11
 * @version 1.0.01
 */
public class DataRenewManager {
	/**
	 * 支持的版本号
	 */
	public final static String SUPPORT_VERSION = "1.0";
	
	private SQLExecutor sqlExecutor;
	
	public DataRenewManager(SQLExecutor sqlExecutor){
		this.sqlExecutor = sqlExecutor;
	}
	
	/**
	 * 从备份文件恢复数据
	 * 不清空表记录
	 * 当发生错误时停止运行
	 * @param is
	 * @throws IOException
	 */
	public void renew(InputStream is) throws IOException {
		if(is == null) throw new IllegalArgumentException("Argument is cannot be null");
		renew(is, false, false, "UTF-8");
	}
	/**
	 * 从备份文件恢复数据
	 * 指定清空表策略
	 * @param is
	 * @param strategy
	 * @throws IOException
	 */
	public void renew(InputStream is, boolean clearBeforeInsert) throws IOException {
		if(is == null) throw new IllegalArgumentException("Argument is cannot be null");
		renew(is, clearBeforeInsert, false, "UTF-8");
	}
	/**
	 * 从备份文件恢复数据
	 * @param is
	 * @param strategy
	 * @param error
	 * @throws IOException
	 */
	public void renew(InputStream is, boolean clearBeforeInsert, boolean skipError) throws IOException {
		if(is == null) throw new IllegalArgumentException("Argument is cannot be null");
		renew(is, clearBeforeInsert, skipError, "UTF-8");
	}
	/**
	 * 从备份文件恢复数据
	 * @param is
	 * @param strategy
	 * @param error
	 * @param encoding
	 * @throws IOException
	 */
	public void renew(InputStream is, final boolean clearBeforeInsert, final boolean skipError, String encoding) throws IOException {
		if(is == null) throw new IllegalArgumentException("Argument is cannot be null");
		
		String encode = encoding == null ? "UTF-8" : encoding;
		final java.io.InputStreamReader isr = new java.io.InputStreamReader(is, encode);
		final java.io.BufferedReader br = new java.io.BufferedReader(isr);
		try{
			sqlExecutor.execute(new SQLExecutor.ConnectionCallback(){
				public Object execute(Connection conn)
						throws SQLException {
					try{
						int index = 1;
						
						long count = 0;

						boolean isStringText = false;
						
						StringBuilder stringText = null;
						
						String sql = null;
						
						long totalRecords = 0;
						
						int currentProcess = 0;
						
						PreparedStatement stmt = null;

						String line = null;
						while( (line = br.readLine()) != null ){
							
							if(line.trim().length() < 1){
								continue;
							}
							
							if(line.startsWith("@")){
								//插入记录之前先清空表记录
								if(clearBeforeInsert){
									clearTables(conn, line);
								}
								//得到总数据行数
								if(line.startsWith("@TotalRecords:")){
									totalRecords = Long.parseLong( line.split(":")[1] );
								}
								continue;
							}
							
							//处理带参数的SQL
							
							if("#STRING:<![CDATA[".equals(line.replaceAll("\\r","").replaceAll("\\n", ""))){
								//System.out.println("BEGINCDATA:");
								isStringText = true;
								stringText = new StringBuilder();
								continue;
							}
							if("]]>".equals(line.replaceAll("\\r","").replaceAll("\\n", ""))){
								//System.out.println("ENDCDATA:");
								isStringText = false;
								stmt.setString(index++, stringText.toString());
								stringText = null;
								continue;
							}
							
							if(isStringText){
								if(stringText.length() > 0){
									stringText.append("\n");
								}
								stringText.append(line);
							}else{
								if(line.startsWith("#")){
									index = setNonStringParam(index, stmt, line);
								}else{
									if(stmt != null){
										try{
											stmt.execute();
											stmt.close();
										}catch(SQLException e){
											if(! skipError){
												throw e;
											}else{
												System.out.println("Skip Error ! SQL: "+sql+"\nError Message: "+e.getMessage());
											}
										}
									}
									stmt = conn.prepareStatement(line);
									sql = line;
									index = 1;
									
									final int percent = (int) ( NumberUtils.floor(++count / (totalRecords*1.0F), 2) * 100);
									if(percent > 0 && percent > currentProcess && percent % 5 == 0){
										currentProcess = percent;
										System.out.println("当前："+currentProcess+"%");
									}
								}
							}
						}
						if(stmt != null){
							stmt.execute();
							stmt.close();
						}
					}catch(IOException e){
						throw new RuntimeException(e);
					}
					
					return null;
				}

				private int setNonStringParam(int index,
						PreparedStatement stmt, String line)
						throws SQLException {
					if(line.startsWith("#DATE:")){
						stmt.setDate( index++, new java.sql.Date(Long.parseLong(line.substring(6))) );
					}else if(line.startsWith("#DATETIME:")){
						stmt.setTimestamp( index++, new java.sql.Timestamp(Long.parseLong(line.substring(10))) );
					}else if(line.startsWith("#TIME:")){
						stmt.setTime( index++, new java.sql.Time(Long.parseLong(line.substring(6))) );
					}else if(line.startsWith("#BOOLEAN:")){
						stmt.setBoolean(index++, Boolean.valueOf(line.substring(9)));
					}else if(line.startsWith("#NULL")){
						stmt.setNull(index++, java.sql.Types.NULL);
					}
					return index;
				}

				private void clearTables(Connection conn, String line)
						throws SQLException {
					if(line.startsWith("@Version")){
						String version = line.split(":")[1];
						if(! isSupportedVersion(version)){
							throw new RuntimeException("Not Supported Version:"+version+", Only Support "+SUPPORT_VERSION+".*");
						}
					}
					if(line.startsWith("@Table:[")){
						String tables = line.substring("@Table:[".length(), line.length()-1);
						String[] spt = tables.split(",");
						Statement temp = conn.createStatement();
						for(int i=spt.length-1; i>=0; i--){
							temp.addBatch("delete from "+spt[i]);
							System.out.println("delete from "+spt[i]);
						}
						temp.executeBatch();
						temp.close();
					}
				}
			});
		}finally{
			br.close();
			isr.close();
			is.close();
		}
	}
	
	private boolean isSupportedVersion(String version){
		return SUPPORT_VERSION.startsWith(version);
	}
	
	public static void showUsage(){
		System.out.println("USAGE：java DataRenewManager -f[ile]=备份文件名 -c[learBeforeInsert]=true|false[default] -s[kipError]=true|false[default] -e[ncoding]=编码［可选，默认为UTF-8］");
	}
	
	public static void main(String[] args) throws Exception {
		if(args.length <1){
			showUsage();
			return;
		}
		String file = null;
		boolean clearBeforeInsert = false;
		boolean skipError = false;
		String encoding = "UTF-8";
		
		for(int i=0; i<args.length; i++){
			String arg = args[i];
			if(arg.startsWith("-f=") || arg.startsWith("-file=")){
				file = arg.split("=", 2)[1];
			}else if(arg.startsWith("-c=") || arg.startsWith("-clearBeforeInsert=")){
				clearBeforeInsert = Boolean.valueOf(arg.split("=", 2)[1]);
			}else if(arg.startsWith("-s=") || arg.startsWith("-skipError=")){
				skipError = Boolean.valueOf(arg.split("=", 2)[1]);
			}else if(arg.startsWith("-e=") || arg.startsWith("-encoding=")){
				encoding = arg.split("=", 2)[1];
			}
		}
		if(file == null){
			showUsage();
			return;
		}
		java.io.InputStream os = new java.io.FileInputStream(file);
		
		new DataRenewManager(new SQLExecutor("db.properties")).renew(os, clearBeforeInsert, skipError, encoding);
	}
}
